(
  function () {
    let upload_inp = document.querySelector('.base64-files')
    let select = document.querySelector('.base64-select')
    let info = document.querySelector('.base64-info')
    let upload = document.querySelector('.base64-upload')
    let sizeInfo = document.querySelector('.base64-size')
    let files = []
    let status = Object.create(null)
    let defaultInfo = '请选择png, jpeg, jpg, gif, icon, tiff, jfif, webp格式文件上传图片(小于200MB, 50个以内), 长按我清空文件列表'
    let size = 0

    upload_inp.addEventListener('change', function (e) {
      console.log(upload_inp.files)

      // 如果此时选择读取了文件,就直接不走if,继续在info里面加信息,并且生成files数组,否则,说明传入了文件的信息
      // 但是是空的, 所以直接显示默认提示, return即可
      if (!upload_inp.files.length) {
        clear(defaultInfo)
        return
      }

      // 有文件进来了, 清空info提示信息, 开始加载, 不可选择
      clear("")
      isUploading(true)

      // 通过.files拿到所有选中的文件
      // 这是一个fileList的列表, 原型先是File后世Blob, 内部有很多属性和方法

      // 这里写一个i用来计数, 每次遍历都会+1, 这样以判断是否结束
      let i = 0
      console.log(i)

      // 过滤的文件列表
      let excludeList = []

      // 遍历FileList转化base64并过滤不合格的数据
      Array.from(upload_inp.files).forEach(function (item, index, arr) {

        // js限制格式----勤用正则表达式的test
        if ((/\.(png|jpe?g|gif?|icon|tiff|webp|jfif)$/).test(item.name)) {

          // 转base64
          let reader = new FileReader()
          reader.readAsDataURL(item)

          // 异步
          reader.onloadend = ev => {

            // 累计一次, 此时已经异步完成转化base64
            i++
            console.log(i)

            // console.log(ev.target)
            // 可以直接用item, index, 在forEach内部,似乎index是保持像let一样的块级作用域的
            // 这里再打印可以看到,index是保持差异的,不会再每一次for循环内index都是不一样的
            console.log(index)
            console.log(item)

            // 得到唯一的key, 能够操作指定的文件信息
            let key = getUniqueKey()

            files.push({
              name: item.name,
              lastModifiedDate: item.lastModifiedDate,
              result: encodeURIComponent(ev.target.result),
              size: ev.total,
              type: item.type,
              // 上key
              key
            })

            console.log(files[files.length - 1])

            info.innerHTML += `
              <li>
                <span key="${key}">${item.name}</span>
                <button class="remove remove-active remove-disabled">remove</button>
              </li>
            `

            // 计算并显示上传文件的大小
            size += item.size

            // 累计显示文件总大小
            sizeInfo.innerText = toSizeInfo(size)


            // 这是异步, 当合法的格式进入这里, 是异步的加载, 而此时不合法的格式在else分支里已经加载完了
            // 所以此时, 就可以判断是否是最后一个元素了
            console.log(i)
            // 如果这是最后一个元素, 说明已经异步的转化base64全部完毕（同步的else分支的累计也完毕）
            if (i === upload_inp.files.length) {
              console.log(files)
              // 把不对的格式和大小不合格过滤或提示
              fileFilter(excludeList)
            }

          }
        }
        else {
          // 对于不合适的格式----不会加入files里, 并加入排除的列表数组
          excludeList.push(item.name)

          // 不合适的格式也累计一次, 注意, 这是同步
          i++
          console.log(i)

          // 这时候是同步的部分i累计完毕了, 如果没有合法的格式, 就不会进入if分支进行异步, 
          // 此时同步累加i的结果就是upload_inp.files.length, 即格式全部不合法
          // 这涉及到同步异步顺序和事件轮询机制
          if (i === upload_inp.files.length) {
            console.log(files)
            // 把不对的格式和大小不合格过滤或提示
            fileFilter(excludeList)
          }
        }
      })

    })


    async function fileFilter(excludeList) {
      let length = upload_inp.files.length

      // 如果格式不一样, 就会在if分支过滤, files数组的长度就不会等于upload_inp.files.length
      if (files.length !== length) {
        // 异步等待文件同步载入结束在进行
        await delay(0)
        alert(`格式已过滤！\n已排除\n${excludeList.join('\n')}`)
      }

      if (files.length) {
        // 限制大小同理-----一byte(字节)计数
        // 10 * 1024 * 1024表示10MB
        if (size >= 200 * 1024 * 1024) {
          // 异步等待文件同步载入结束在进行
          await delay(0)
          alert('文件总量须小于200MB!请删减')
          status.size = 'File too large'
        }
        else {
          await delay(0)
          alert('文件加载完毕')
        }
      }
      else {
        clear(defaultInfo)
      }

      // 文件加载完毕, 可以上传了
      isUploading(false)
    }


    // 利用事件委托: remove的button在info的div里,会冒泡,所以只要点击了remove的button,info的div也会被点击到
    // 此时只要对比target就行了
    // 什么时候使用事件委托呢?
    // 当有很多相同的事件需要监听的时候,比如这里的列表的button,如果每一个都监听,那么内存会给每一个监听器(含回调函数)都开辟空间
    // 在触发时回调,相当于一个方法拷贝了n份,而直接在父元素使用事件委托,就只有一个事件监听器,开销会小很多
    info.addEventListener('click', (e) => {

      // console.log(e.target.className)
      // 移除
      if (e.target.className.includes('remove')) {
        let key = e.target.previousElementSibling.getAttribute('key')
        console.log(e.target.previousElementSibling.getAttribute('key'))
        files = files.filter(item => {
          // 如果匹配到删减的文件, 就减去对应的大小, 并且在判断是否小于了100MB, 如果小于100MB就删除status.size, 
          // 这样就点击upload判断时, 就可以上传了
          if (item.key === key) {
            size -= item.size
            sizeInfo.innerText = toSizeInfo(size)
            size < 200 * 1024 * 1024 && delete status.size
            console.log(size, status.size, files)
            e.target.parentElement.remove()
          }

          return !(item.key === key)
        })

        if (!files.length) {
          info.innerText = defaultInfo
        }
      }

    })

    // 利用定时器长按1秒清空
    let clearAll
    info.addEventListener('mousedown', (e) => {
      if (files.length) {
        clearAll = setTimeout(() => {
          clear(defaultInfo)
          delete status.size
        }, 1000)
      }
    })
    info.addEventListener('mouseup', (e) => {
      if (clearAll) {
        clearTimeout(clearAll)
        clearAll = null
      }
    })

    // 绑定选择事件
    console.log(handleSelect(select, upload_inp))

    upload.addEventListener('click', function (e) {

      if (!upload.classList.contains('active') || !select.classList.contains('active')) {
        return
      }

      // 相当于防抖, 只要上传,只有上传之后才会可点击状态
      if (!files.length) {
        alert('上传文件为空, 请选择文件!')
      }
      else {

        // 如果文件太大，直接return，不上传
        if (status.size) {
          alert(status.size + ', long press to clear all files')
          return;
        }

        isUploading(true)
        console.log(upload_inp.files)
        axios.post('/upload_base64', { files, fqm: 'fqmyysjjd' }, {
          Headers: {
            'Content-Type': 'multipart/form-data',
          }
        }).then(res => {
          if (res.data.ok) {
            console.log(res.data.info.join('\n'))
            console.log('上传完毕~')
            // alert('Serve path:\n' + res.data.info.join('\n'))
          }
          else {
            throw res.data.info
          }
        }).catch(e => {
          // 这里不仅采集前端浏览器请求时抛出的错误, 还把服务器返回的错误（ok：0）也给弄了
          console.log(e)
          alert('something was wrong')
        }).finally(() => {
          isUploading(false)
          clear(defaultInfo)
        })

      }
    })


    // 上传时按钮禁用
    function isUploading(bool) {
      select.disabled = bool
      upload.disabled = bool
      if (bool) {
        select.classList.remove('active')
        upload.classList.remove('active')
        select.classList.add('file-bnt-disabled')
        upload.classList.add('file-bnt-disabled')
        
        document.querySelectorAll('.remove').forEach((item, index, arr) => {
          arr[index].disabled = bool
          arr[index].classList.remove('remove-active')
          arr[index].classList.add('remove-disabled')
        })
      }
      else {
        select.classList.add('active')
        upload.classList.add('active')
        select.classList.remove('file-bnt-disabled')
        upload.classList.remove('file-bnt-disabled')

        document.querySelectorAll('.remove').forEach((item, index, arr) => {
          arr[index].disabled = bool
          arr[index].classList.add('remove-active')
          arr[index].classList.remove('remove-disabled')
        })
      }
    }

    // 清除信息
    function clear(defaultInfo) {
      files = []
      info.innerText = defaultInfo
      size = 0
      sizeInfo.innerText = '0MB'
    }

  }
)()